
from EngineDC import *

#import SafeEval

the_engine = Engine()

# Possible input commands
#   Player commands (e.g., up, fire, jetpack)
#   Game commands (e.g., quit, display menu, pause)

#createInputCommand("player.up", self.player.giveCommand)
#createInputCommand("select_button_down")
#createInputCommand("action_button_up")

#bindInputCommandToKey("player.up", pygame.K_w)


def curry(func,*args,**kwargs):
    def curried(*moreargs,**morekwargs):
        morekwargs.update(kwargs)
        return func(*(moreargs+args),**morekwargs)
    return curried

class Player(object):
    def __init__(self,name):
        self.name = name
        self.entity = None

the_player = Player('someone')

the_player2 = Player('someone_else')


class Game(object):

    def __init__(self):
        # Create game engine instance
        #self.engine = Engine()

        # TODO: Remove this crud
        #the_engine.addEventListener('render', self.render)
        #the_engine.addEventListener('tick', self.tick)
        
        # Engine settings
        the_engine.tickRate = 60
        the_engine.gravity = -30.0
        the_engine.worldMinX = -20.0
        the_engine.worldMinY = -20.0
        the_engine.worldMaxX = 20.0
        the_engine.worldMaxY = 20.0
        # TODO: Work out why turning this on makes the physics go haywire
        the_engine.usePhysicsSleeping = False
        the_engine.windowWidth = 800
        the_engine.windowHeight = 600
        the_engine.forceResolution = True
        the_engine.fullscreen = False
        #the_engine.physicsIterations = 10
        the_engine.velocity_iterations = 10
        the_engine.position_iterations = 8
        the_engine.init()

        the_engine.renderer.enableParallax(True, True)

        # Set up the render layers
        the_engine.renderer.addLayer("background", 0)
        the_engine.renderer.addLayer("distant", 0.5)
        the_engine.renderer.addLayer("behind", 1)
        the_engine.renderer.addLayer("play", 1)
        the_engine.renderer.addLayer("infront", 1)
        the_engine.renderer.addLayer("foreground", 2.0)
        the_engine.renderer.addLayer("GUI", 0)

        the_engine.renderer.disableMotionBlur()

        # Set up the camera
        the_engine.renderer.camera.setMaxZoomRate(0.01)
        the_engine.renderer.camera.setZoomAccel(0.02)
        the_engine.renderer.camera.zoomTo(0.1)

        # TODO: Remove this and replace with the level's solid_geometry
        groundShapeDef = b2PolygonDef()
        groundShapeDef.SetAsBox(40.0, 0.1, b2Vec2(0.0, -1.0), 0.0)
        the_engine.physics_world.GetGroundBody().CreateShape(groundShapeDef)
        the_engine.physics_world.GetGroundBody().SetUserData(None)

        world = the_engine.world

        #the_engine.renderer.camera.followBody(self.playerChar.referenceBody)

        #self.soldiers = []
        # Create two teams of Soldiers

        world.createEntity(Bunny, 'playerbun', the_player, b2Vec2(-5.0,5.0))
        world.createEntity(Bunny, 'otherbun', the_player2, b2Vec2(5.0,5.0))
        #level = world.createEntity(Level, "level")
        #level.load("Level1")

    def quit(self):
        the_engine.running = False



gamerules = the_engine.engine_event_rules
gamerules.bindings = the_engine.keyboard_bindings
gamerules.mbindings = the_engine.mouse_bindings

class Command(object):
    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

class Bunny(CompositeBody, Commandable):
    def __init__(self, engine, name, player, pos):
        self.player = player
        self.color = (0.5, 0.5, 1.0, 1)
        CompositeBody.__init__(self, engine, name)
        self.addBody('body', self.engine.bodyLoader. \
                     createCircleBody(pos, radius=0.5, density=1), \
                     make_reference_body = True)
        self.models['body'] = self.engine.renderer. \
                     createModel(layer="play", \
                                 mesh="soldier", \
                                 texture="soldier_standing", \
                                 influences=[(self.ref_body.GetXForm(), 1.0)], \
                                 local_xform=b2XForm_identity, \
                                 color=self.color)
        Commandable.__init__(self,['move','jump'])

        self.max_speed = 4
        self.max_jump = 7
        self.jump_force = 150
        self.accel = 50
        self.size = 1 # bunny diameter
        self.on_ground = False
        self.jump_pending = False
        
        @self.command_rules.rule('onleft')
        def onLeft(self):
            self.has_command['right'] = False
            if self.getLinearVelocity().x>-self.max_speed:
                self.applyCenteredForce(b2Vec2(-self.accel,0.0))
        @self.command_rules.rule('onright')
        def onRight(self,target):
            self.has_command['left'] = False
            if self.getLinearVelocity().x<self.max_speed:
                self.applyCenteredForce(b2Vec2(self.accel,0.0))
        @self.command_rules.rule('givejump')
        def giveJump(self):
            if not self.isOnGround(): self.jump_pending = True
            print self.isOnGround()
        @self.command_rules.rule('onjump')
        def onJump(self):
            if self.jump_pending:
                if self.isOnGround(): self.jump_pending = False
            else: self.applyCenteredForce(b2Vec2(0.0,self.jump_force))
            if self.getLinearVelocity().y > self.max_jump:
                self.has_command['jump'] = False

        
    @gamerules.eventMaker
    def die(self):
        print "Unit", self.name, "has died."

    def isOnGround(self):
        return self.on_ground


    '''def giveCommand(self, command):
        #print 'command',command.name,command.__dict__
        Commandable.giveCommand(self,command=command.name,value=True,**command.__dict__)
    '''
    def update(self, timeStep):
        # Turn the body to face the direction of motion
        if self.getLinearVelocity().Length() > 0.0:
            vel = self.getLinearVelocity()
            angle = -math.atan2(vel.x, vel.y)
            self.setXForm(b2XForm(self.getPos(), b2Mat22(angle)))
            #self.applyTorque(angle - self.getAngle())
            #self.applyDampingTorque(0.1)

        #self.applyDampingForce(1.0)
        
        # Obey commands
        self.updateCommands()








# These commands are essential to make the binding system actually work,
# probably shouldn't be here


@gamerules.eventMaker
def commandGiver(start,player,command):
    player.entity.giveCommand(command, start)

@gamerules.rule(pygame.KEYDOWN)
def handleInput(event):
    try: gamerules.bindings[event.key](True)
    except KeyError: print 'Key',chr(event.key)if event.key<256 else'','not bound'
    
@gamerules.rule(pygame.KEYUP)
def handleInput(event):
    try: gamerules.bindings[event.key](False)
    except KeyError: pass
    
@gamerules.rule(pygame.MOUSEBUTTONDOWN)
def handleMouseDown(event):
    try: gamerules.mbindings[event.button](True,event.pos)
    except KeyError: pass

@gamerules.rule(pygame.MOUSEBUTTONUP)
def handleMouseUp(event):
    try: gamerules.mbindings[event.button](False,event.pos)
    except KeyError: pass

#@gamerules.rule(pygame.MOUSEMOTION)
def handleMouseMove(event):
    gamerules.mbindings[4](event.pos) #TODO: fix mouse button binding

@gamerules.rule(pygame.QUIT)
def quitGame(event):
    game.quit()


# test implentation of a messaging system

the_engine.message = ''
the_engine.messaging = False

@gamerules.rule(pygame.KEYDOWN)
def sendMessage(event):
    if not the_engine.messaging: return
    if event.key == pygame.K_RETURN:
        print 'a message:',the_engine.message
        the_engine.message = ''
        the_engine.messaging = False
    else: the_engine.message += chr(event.key)

def startMessage(tf):
    the_engine.messaging = True

def mbdown(tf,pos):
    print pos

def mmove(pos):
    print 'moving: ',pos

def info(tf):
    print 'entity position:', the_player.entity.getPos()


# actual bindings are here

gamerules.bindings[pygame.K_d] = curry(commandGiver,the_player,'right')
gamerules.bindings[pygame.K_a] = curry(commandGiver,the_player,'left')
gamerules.bindings[pygame.K_w] = curry(commandGiver,the_player,'jump')
gamerules.bindings[pygame.K_RIGHT] = curry(commandGiver,the_player2,'right')
gamerules.bindings[pygame.K_LEFT] = curry(commandGiver,the_player2,'left')
gamerules.bindings[pygame.K_UP] = curry(commandGiver,the_player2,'jump')
gamerules.bindings[pygame.K_i] = info
#gamerules.bindings[pygame.K_s] = curry(commandGiver,PlayerChar.CMD_DOWN)
#gamerules.bindings[303] = curry(commandGiver,PlayerChar.CMD_UP)
#gamerules.bindings[303] = curry(commandGiver,PlayerChar.CMD_DOWN)
#gamerules.mbindings[1] = select
#gamerules.mbindings[3] = order
gamerules.bindings[pygame.K_m] = startMessage
#gamerules.bindings[4] = mmove

@gamerules.rule('addedcollision')
def bunnyCollision(force,entity1,entity2):
    if isinstance(entity1,Bunny) and isinstance(entity2,Bunny): # TODO: make better game logic
        x1,y1 = entity1.getPos().x,entity1.getPos().y
        x2,y2 = entity2.getPos().x,entity2.getPos().y
        if y1>y2:# and (x1-x2)**2 < (entity1.size+entity2.size)/2:
            entity2.die()
        if y2>y1:# and (x1-x2)**2 < (entity1.size+entity2.size)/2:
            entity1.die()

@gamerules.rule('die')
def bunnyDie(the_event):
    the_event.terminate = True

@gamerules.rule('die')
def bunnyDie(self):
    the_engine.world.destroyEntity(self)
    self.player.entity = the_engine.world.createEntity(Bunny, None, self.player, b2Vec2(-5.0*random.random(),5.0*random.random()))
    self.applyCenteredForce(b2Vec2(-1000,0.0))

'''
@gamerules.rule('addedcollision')
def groundCheck(force,entity1,entity2): # BLOODY AWFUL
    print force
    if isinstance(entity1,Bunny) and force.y<0 and force.y**2>force.x**2: entity1.on_ground = True
    if isinstance(entity2,Bunny) and force.y>0 and force.y**2>force.x**2: entity2.on_ground = True
        
@gamerules.rule('persistingcollision')
def groundCheck(force,entity1,entity2):
    if isinstance(entity1,Bunny) and force.y<0 and force.y**2>force.x**2: entity1.on_ground = True
    if isinstance(entity2,Bunny) and force.y>0 and force.y**2>force.x**2: entity2.on_ground = True    
'''

@gamerules.rule('collision')
def groundCheck(force,entity1,entity2):
    if isinstance(entity1,Bunny) and force.y<0 and force.y**2>force.x**2: entity1.on_ground = True
    if isinstance(entity2,Bunny) and force.y>0 and force.y**2>force.x**2: entity2.on_ground = True    


@gamerules.rule('removedcollision')
def groundCheck(force,entity1,entity2): # REALLY TERRIBLY BAD
    if isinstance(entity1,Bunny): entity1.on_ground = False
    if isinstance(entity2,Bunny): entity2.on_ground = False

        
game = Game()

the_player.entity = the_engine.world.entities['playerbun']
the_player2.entity = the_engine.world.entities['otherbun']

the_engine.run()



